﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using JasRibbonApplicatie.Classes;
using JasRibbonApplicatie.Model;

namespace JasRibbonApplicatie.Controllers
{
	public class OntleningBeheerderBackup
	{

		public Aanvraag HuidigeAanvraag { get; set; }
		public Magazijnier HuidigeMagazijnier { get; set; }

		public List<OntleningDataBackup<Materiaaltype>> OntleningDataMaterialen { get; set; }
		public List<OntleningDataBackup<ToebehorenType>> OntleningDataToebehoren { get; set; }

		public List<String> lijstScancodes;

        private static MateriaalModel mm = new MateriaalModel();
        private static AanvraagModel am = new AanvraagModel();

		// alle materialen en toebehoren worden opgehaald bij een aanvraag;
		// daarna ook de reeds ontleende materialen (die eventueel ook al binnengebracht zijn)....
		public OntleningBeheerderBackup(Aanvraag aanvraag, Magazijnier magazijnier)
		{
			// controleren of aanvraag = null? :: voorkomen van programmeerfouten in de toekomst...? (andere volgorde van actief zetten van controls levert reeds problemen op
			// => beter dat misschien aanpakken
			this.HuidigeAanvraag = aanvraag;
			this.HuidigeMagazijnier = magazijnier;

			this.OntleningDataMaterialen = new List<OntleningDataBackup<Materiaaltype>>();
			this.OntleningDataToebehoren = new List<OntleningDataBackup<ToebehorenType>>();

			// aanvraag: bestelde materialen verwerken
			List<MateriaalBestelling> materiaalbestellingen = mm.GetMateriaalBestellingenByAanvraagId(HuidigeAanvraag.aanvraag_id);
			foreach (var bestelling in materiaalbestellingen)
			{             
				Materiaaltype materiaaltype = bestelling.materiaaltype;
				int index = this.GetIndexProducttypeAndAddIfNeeded<Materiaaltype>(materiaaltype, OntleningDataMaterialen);

				OntleningDataMaterialen.ElementAt(index).AantalGoedgekeurd += bestelling.aantalGoedgekeurd;

				var lijstToebehoren = materiaaltype.toebehorenmateriaal;
				foreach (ToebehorenMateriaal toebehorenMateriaal in lijstToebehoren)
				{
                    index = this.GetIndexProducttypeAndAddIfNeeded<ToebehorenType>(toebehorenMateriaal.toebehorentype, OntleningDataToebehoren);
					OntleningDataToebehoren.ElementAt(index).AantalGoedgekeurd += toebehorenMateriaal.aantal;
				}
			}
			// bestellingen afgehandeld

			// reeds uitgeleende producten ophalen (de producten die terug binnengebracht zijn worden momenteel niet opgehaald
			List<Ontlening> lijstOntleningen = mm.GetOntleendeProductenByAanvraagId(this.HuidigeAanvraag.aanvraag_id);
			lijstScancodes = new List<string>();
			foreach (var ontlening in lijstOntleningen) // er wordt vanuitgegaan dat voor alle ontleningen ook een aanvraag met materiaalbestelling is....
			{
				// anders is deze scancode reeds weer verdwenen
				if (ontlening.tijdstip_inlevering.Equals(DateTime.MinValue))
				{
					lijstScancodes.Add(ontlening.streepjescode);
				}
				
				char eersteKarakter = ontlening.streepjescode.ToLower()[0];
				bool reedsTerugBinnen = (!(ontlening.tijdstip_inlevering.Equals(DateTime.MinValue)));
				if (eersteKarakter.Equals('m'))
				{
					Materiaaltype materiaaltype = mm.GetMateriaalTypeByMateriaalScancode(ontlening.streepjescode);
					this.ontleningenInstellenUitDatabase<Materiaaltype>(OntleningDataMaterialen, materiaaltype, reedsTerugBinnen);
				}
				else if (eersteKarakter.Equals('t'))
				{
					ToebehorenType toebehorentype = mm.GetTypeToebehorenByToebehorenScancode(ontlening.streepjescode);
					this.ontleningenInstellenUitDatabase<ToebehorenType>(OntleningDataToebehoren, toebehorentype, reedsTerugBinnen);
				}
			}
		}


		// return: 'false' wanneer de ontlening niet mag doorgaan!
		// throws ArgumentException wanneer het product niet in de database gevonden kan worden, of wanneer
		// het aantalOntleningen het aantalGoedgekeurd bereikt heeft...
		public bool OntleningToevoegen(String productScancode)
		{
			String code = productScancode.ToLower();
			bool gevonden = false;
			if (!lijstScancodes.Contains(code)) // controleren of product niet reeds ingescand is
			{
				if (code.First().Equals('m')) // materiaal
				{
					Materiaaltype producttype = mm.GetMateriaalTypeByMateriaalScancode(code);
					gevonden = OntleningGeorganiseerdToevoegen<Materiaaltype>(producttype, OntleningDataMaterialen);
				}
				else if (code.First().Equals('t')) // toebehoren
				{
					ToebehorenType typeToebehoren = mm.GetTypeToebehorenByToebehorenScancode(code);
					gevonden = OntleningGeorganiseerdToevoegen<ToebehorenType>(typeToebehoren, OntleningDataToebehoren);
				}
				else
					throw new ArgumentException("Er dient een geldige productcode ingescand te worden!");

				if (gevonden)
				{
					if (mm.IsProductNietUitgeleendMomenteel(code))
					{
						lijstScancodes.Add(code);
						am.AddOntlening(this.HuidigeAanvraag.aanvraag_id, code, this.HuidigeMagazijnier.magazijnier_id);
						return true;
					}
					else throw new ArgumentException("Product is momenteel reeds uitgeleend!");
				}
			}
			else
			{
				throw new ArgumentException("Product is reeds uitgescand...");
			}
			return false;
		}

		// return false indien producttype niet gevonden in ontlening, of indien dit soort materiaal reeds te veel keer
		// uitgeleend is (aantalOntleend >= aantalGoedgekeurd)
		public bool OntleningGeorganiseerdToevoegen<X>(X producttype, List<OntleningDataBackup<X>> lijstProducttypes)
		{
			bool gevonden = false;
			if (producttype != null)
				{
					int i = 0;
					while ((i < lijstProducttypes.Count) && (!gevonden))
					{
						if (lijstProducttypes.ElementAt(i).Producttype.Equals(producttype))
							gevonden = true;
						else
							i++;
					}
					if (gevonden)
						if (lijstProducttypes.ElementAt(i).AantalOntleend < lijstProducttypes.ElementAt(i).AantalGoedgekeurd)
						{
							lijstProducttypes.ElementAt(i).AantalOntleend += 1;
						}
						else
							throw new ArgumentException("Producttype kan niet (meer) uitgeleend worden - zie AantalGoedgekeurd...");                       
				}
				else
					throw new ArgumentException("Ingescande productcode kan niet in de database gevonden worden...");
			return gevonden;
		}

		public bool OntleningBeeindigen(String productScancode)
		{
			if (lijstScancodes.Contains(productScancode))
			{
				// dan is het sowieso een geldige scancode
				if (productScancode.ToLower().First().Equals('m'))
				{
					Materiaaltype producttype = mm.GetMateriaalTypeByMateriaalScancode(productScancode);
					int index = GetIndexProducttypeAndAddIfNeeded<Materiaaltype>(producttype, OntleningDataMaterialen);
					OntleningDataMaterialen[index].AantalBinnengebracht += 1;
				}
				else
				{
					ToebehorenType producttype = mm.GetTypeToebehorenByToebehorenScancode(productScancode);
					int index = GetIndexProducttypeAndAddIfNeeded<ToebehorenType>(producttype, OntleningDataToebehoren);
					OntleningDataToebehoren[index].AantalBinnengebracht += 1;
				}
				am.BeeindigOntlening(this.HuidigeAanvraag.aanvraag_id, productScancode, this.HuidigeMagazijnier.magazijnier_id);
				lijstScancodes.Remove(productScancode); // mag niet nogmaals beeindigd worden
			}
			else
			{
				// deze error nog uit elkaar trekken
				throw new ArgumentException("Dit product bestaat niet, is niet uitgescand geweest voor deze aanvraag, of is reeds terug ingeleverd!");
			}
			return true;
		}

		private int GetIndexProducttypeAndAddIfNeeded<X>(X producttype, List<OntleningDataBackup<X>> lijstOntleningData)
		{
			bool gevonden = false;
			int index = 0;
			while ((index < lijstOntleningData.Count) && (!gevonden))
			{
				if (lijstOntleningData.ElementAt(index).Producttype.Equals(producttype))
				{
					gevonden = true;
				}
				else
					index++;
			}
			// als producttype niet gevonden is, moet dit producttype nog toegevoegd worden aan de lijst...
			if (!gevonden)
			{
				lijstOntleningData.Add(new OntleningDataBackup<X>(producttype));
				index = lijstOntleningData.Count - 1; // want de index is gevraagd
			}
			return index;
		}

		private void ontleningenInstellenUitDatabase<X>(List<OntleningDataBackup<X>> lijstOntleningData, X producttype, bool reedsTerugBinnen)
		{
			int i = 0;
			bool gevonden = false;
			while ((i < lijstOntleningData.Count) && (!gevonden))
			{
				if (lijstOntleningData.ElementAt(i).Producttype.Equals(producttype))
				{
					lijstOntleningData.ElementAt(i).AantalOntleend += 1;
					if (reedsTerugBinnen)
						lijstOntleningData.ElementAt(i).AantalBinnengebracht += 1;
				}
				i++;
			}
		}

		public void RemoveOntlening(String scancode)
		{
			bool codeBestaat = false;
            if (scancode.Count() == 5)
            {
                if (scancode.ToLower().First().Equals('m')) // materiaal
                {
                    Materiaaltype producttype = mm.GetMateriaalTypeByMateriaalScancode(scancode);
                    int index = GetIndexProducttypeAndAddIfNeeded<Materiaaltype>(producttype, OntleningDataMaterialen);
                    OntleningDataMaterialen[index].AantalOntleend -= 1;
                    codeBestaat = true;
                }
                else if (scancode.ToLower().First().Equals('t')) // toebehoren
                {
                    ToebehorenType typeToebehoren = mm.GetTypeToebehorenByToebehorenScancode(scancode);
                    int index = GetIndexProducttypeAndAddIfNeeded<ToebehorenType>(typeToebehoren, OntleningDataToebehoren);
                    OntleningDataToebehoren[index].AantalOntleend -= 1;
                    codeBestaat = true;
                }
                if (codeBestaat)
                {
                    am.UndoAddOntlening(this.HuidigeAanvraag.aanvraag_id, scancode);
                    // ontlening verwijderen uit database
                    lijstScancodes.Remove(scancode);
                }
            }

		}

		public void UndoBeeindigingOntlening(String scancode)
		{
			bool codebestaat = false;
            if (scancode.Count() == 5)
            {
                if (scancode.ToLower().First().Equals('m')) // materiaal
                {
                    Materiaaltype producttype = mm.GetMateriaalTypeByMateriaalScancode(scancode);
                    int index = GetIndexProducttypeAndAddIfNeeded<Materiaaltype>(producttype, OntleningDataMaterialen);
                    OntleningDataMaterialen[index].AantalBinnengebracht -= 1;
                    codebestaat = true;
                }
                else if (scancode.ToLower().First().Equals('t')) // toebehoren
                {
                    ToebehorenType typeToebehoren = mm.GetTypeToebehorenByToebehorenScancode(scancode);
                    int index = GetIndexProducttypeAndAddIfNeeded<ToebehorenType>(typeToebehoren, OntleningDataToebehoren);
                    OntleningDataToebehoren[index].AantalBinnengebracht -= 1;
                    codebestaat = true;
                }
                if (codebestaat)
                {
                    am.UndoBeeindigingOntlening(this.HuidigeAanvraag.aanvraag_id, scancode);
                    // magazijnier en tijdstip terug op beginwaarde instellen
                    lijstScancodes.Add(scancode); // is dan dus weer uitgeleend op dat moment
                }
            }
		}
	}
}
